package com.gitee.wsl.security.hash.bcrypt

import com.gitee.wsl.lang.bytearray.Bytes
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals


class BCryptFormatterTest {

    private val formatter: BCryptFormatter = BCryptFormatter.Default(Radix64Encoder.Default())
    private val parser: BCryptParser = BCryptParser.Default(Radix64Encoder.Default())

    @Test
    @Throws(IllegalBCryptFormatException::class)
    fun createRandomMessageAndVerify() {
        val cost: Int = Random.nextInt(27) + 4
        val salt: ByteArray = Bytes.random(16).array()
        val hash: ByteArray = Bytes.random(23).array()
        val version: BCrypt.Version =
            BCrypt.Version.SUPPORTED_VERSIONS[Random.nextInt(BCrypt.Version.SUPPORTED_VERSIONS.size)]
        val hashData = BCrypt.HashData(cost, version, salt, hash)
        val bcryptHash: ByteArray = formatter.createHashMessage(hashData)
        val parsed = parser.parse(bcryptHash)

        assertEquals(hashData, parsed)
    }

    @Test
    fun testAgainstReferenceHash1() {
        testAgainstReferenceHash(
            BCrypt.HashData(
                6, BCrypt.Version.VERSION_2A,
                byteArrayOf(
                    0x14,
                    0x4B,
                    0x3D,
                    0x69,
                    0x1A,
                    0x7B,
                    0x4E,
                    0xCF.toByte(),
                    0x39,
                    0xCF.toByte(),
                    0x73,
                    0x5C,
                    0x7F.toByte(),
                    0xA7.toByte(),
                    0xA7.toByte(),
                    0x9C.toByte()
                ),
                byteArrayOf(
                    0x55,
                    0x7E,
                    0x94.toByte(),
                    0xF3.toByte(),
                    0x4B,
                    0xF2.toByte(),
                    0x86.toByte(),
                    0xE8.toByte(),
                    0x71,
                    0x9A.toByte(),
                    0x26,
                    0xBE.toByte(),
                    0x94.toByte(),
                    0xAC.toByte(),
                    0x1E,
                    0x16,
                    0xD9.toByte(),
                    0x5E,
                    0xF9.toByte(),
                    0xF8.toByte(),
                    0x19,
                    0xDE.toByte(),
                    0xE0.toByte()
                )
            ),
            "$2a$06\$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."
        )
    }

    @Test
    fun testAgainstReferenceHash2() {
        testAgainstReferenceHash(
            BCrypt.HashData(
                12, BCrypt.Version.VERSION_2Y,
                byteArrayOf(
                    0x17,
                    0xA2.toByte(),
                    0x3B,
                    0x87.toByte(),
                    0x7F.toByte(),
                    0xAA.toByte(),
                    0xF5.toByte(),
                    0xC3.toByte(),
                    0x8E.toByte(),
                    0x87.toByte(),
                    0x27,
                    0x2E,
                    0x0C,
                    0xDF.toByte(),
                    0x48,
                    0xAF.toByte()
                ),
                byteArrayOf(
                    0x49,
                    0x8C.toByte(),
                    0x11,
                    0xE6.toByte(),
                    0xB9.toByte(),
                    0xAD.toByte(),
                    0x6E,
                    0xD4.toByte(),
                    0x02,
                    0xA6.toByte(),
                    0xC4.toByte(),
                    0x40,
                    0x76,
                    0x88.toByte(),
                    0x35,
                    0x74,
                    0xEA.toByte(),
                    0x62,
                    0x01,
                    0x2C,
                    0x8B.toByte(),
                    0x06,
                    0xB2.toByte()
                )
            ),
            "$2y$12\$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"
        )
    }

    @Test
    fun testAgainstReferenceHash3() {
        testAgainstReferenceHash(
            BCrypt.HashData(
                8, BCrypt.Version.VERSION_2B,
                byteArrayOf(
                    0x26,
                    0xC6.toByte(),
                    0x30,
                    0x33,
                    0xC0.toByte(),
                    0x4F,
                    0x8B.toByte(),
                    0xCB.toByte(),
                    0xA2.toByte(),
                    0xFE.toByte(),
                    0x24,
                    0xB5.toByte(),
                    0x74,
                    0xDB.toByte(),
                    0x62,
                    0x74
                ),
                byteArrayOf(
                    0x56,
                    0x70,
                    0x1B,
                    0x26,
                    0x16,
                    0x4D,
                    0x8F.toByte(),
                    0x1B,
                    0xC1.toByte(),
                    0x52,
                    0x25,
                    0xF4.toByte(),
                    0x62,
                    0x34,
                    0xAC.toByte(),
                    0x8A.toByte(),
                    0xC7.toByte(),
                    0x9B.toByte(),
                    0xF5.toByte(),
                    0xBC.toByte(),
                    0x16,
                    0xBF.toByte(),
                    0x48
                )
            ),
            "$2b$08\$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"
        )
    }

    private fun testAgainstReferenceHash(hashData: BCrypt.HashData, refHash: String) {
        val bcryptHash: ByteArray = formatter.createHashMessage(hashData)
        assertContentEquals(refHash.encodeToByteArray(), bcryptHash)
    }
}
